package bsh.utils.reflection.transform;

import java.lang.reflect.Method;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import bsh.CompilationState;
import bsh.dyn.DynamicObject;
import bsh.exception.CompilationAssertionFailed;
import bsh.meta.JavaClassWrapper;
import bsh.meta.TypeTransformation;
import bsh.meta.TypeWrapper;

/**
 * {@link TypeTransformation} para um tipo dinamico
 * 
 * @author takeshi
 */
public class DynamicWrapperTypeTransformation implements TypeTransformation {

    private static final TypeWrapper TO_TYPE = JavaClassWrapper.wrapperFor(DynamicObject.class);

    private static final Method WRAPPER_METHOD;

    static {
        try {
            WRAPPER_METHOD = DynamicObject.class.getMethod("wrap", Object.class);
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean acceptsType(TypeWrapper type) {
        return !type.isPrimitive() && !TO_TYPE.equals(type) && type != TypeWrapper.NULL_TYPE_WRAPPER;
    }

    /**
     * {@inheritDoc}
     */
    public TypeWrapper toType(TypeWrapper fromType) {
        return TO_TYPE;
    }

    /**
     * {@inheritDoc}
     */
    public TypeWrapper transformStackTop(MethodVisitor visitor, TypeWrapper fromType, CompilationState state) {
        TypeWrapper popStack = state.popStack();
        if (TO_TYPE.isSuperTypeOf(popStack)) {
            throw new CompilationAssertionFailed("trying to wrap a wrapper");
        }
        visitor.visitMethodInsn(Opcodes.INVOKESTATIC, TO_TYPE.internalName(), WRAPPER_METHOD.getName(), Type
                .getMethodDescriptor(WRAPPER_METHOD));
        state.pushStack(TO_TYPE);
        return TO_TYPE;
    }

}
